package j.util.jdbc.simple.metadata;

import j.util.New;
import j.util.StringUtils;
import j.util.jdbc.simple.annotation.*;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 元数据解析类
 * 注解中的字段引用的是实体的属性而非数据库中的字段名
 * @author 刘军海
 * @version Sep 13, 2011 8:56:37 AM
 */
public class MetaDataUtils {
	
	private static final String INVALID_FIELD_MSG = "entityAttribute [%1$s] dones't have the corrected map,please check.The entityAttribute must be entity's field name.";

	/**
	 * 根据实体类的反射类class中的注解Table/Inser/Update/Query等解析出表的元数据信息，
	 * 这里的元数据不是数据库中所定义的元数据，而是由开发人员在实体中以注解的形式所定义的元数据信息
	 * @param clazz
	 * @return
	 */
	public static <T> TableMetaData parseTableMetaData(Class<T> clazz){
		Table table = (Table)clazz.getAnnotation(Table.class);
		TableMetaData tmd = null;
		Map<String, Column> columns = New.lMap();
		//实体属性与数据库字段的映射
		Map<String,String> entityKeyMap = New.map();
		//从实体中解析出来所有的可用字段，并生成实体属性与数据库字段的映射map
		parseEntityColumns(columns,entityKeyMap,clazz,false);
		if(null == table){
			tmd = new TableMetaData(null,getOnlyClassName(clazz.getName()));
		}else{
			tmd = new TableMetaData(table.schemaName(),table.name());
			tmd.setTree(table.isTree());
			if(tmd.isTree()){
				tmd.setParentFieldName(table.parentFieldName());
				tmd.setLeftFieldName(table.leftFieldName());
				tmd.setRightFieldName(table.rightFieldName());
			}
			parseTableMetaData(tmd,entityKeyMap,table,clazz);
		}
		//将实体属性与数据库字段的映射关系设置到表元数据中
		tmd.setEntityAttributeColumnMap(entityKeyMap);
		tmd.setColumns(columns);
		//解析Insert注解
		parseInsertAttribute(tmd,entityKeyMap,clazz);
		//解析Update注解
		parseUpdateAttribute(tmd,entityKeyMap,clazz);
		//解析Query注解
		parseQueryAttribute(tmd,entityKeyMap,clazz);
		return tmd;
	}
	/**
	 * 解析Update注解
	 * @param tmd
	 * @param entityKeyMap
	 * @param clazz
	 */
	private static <T> void parseUpdateAttribute(TableMetaData tmd,Map<String,String> entityKeyMap,Class<T> clazz){
		UpdateAttribute ua = new UpdateAttribute();
		Update update = (Update)clazz.getAnnotation(Update.class);
		if(null!=update){
			ua.setComparedToOld(update.comparedToOld());
			ua.setChecked(update.checked());
			//解析insert中的可用字段
			ua.setUsedColumns(computeUsedColumns("insert",
					entityKeyMap,
					update.usedColumns(),update.noUsedColumns())
				);
		}
		tmd.setUpdateAttribute(ua);
	}
	/**
	 * 解析Query注解
	 * @param tmd
	 * @param entityKeyMap
	 * @param clazz
	 */
	private static <T> void parseQueryAttribute(TableMetaData tmd,Map<String,String> entityKeyMap,Class<T> clazz){
		QueryAttribute qa = new QueryAttribute();
		Query query = (Query)clazz.getAnnotation(Query.class);
		if(null!=query){
			qa.setUsedColumnsForList(computeUsedColumns("query",
					entityKeyMap,
					query.usedColumnsForList(),query.nousedColumnsForList())
				);
		}
		tmd.setQueryAttribute(qa);
	}
	/**
	 * 解析实体中Insert注解
	 * @param tmd
	 * @param entityKeyMap
	 * @param clazz
	 */
	private static <T> void parseInsertAttribute(TableMetaData tmd,Map<String,String> entityKeyMap,Class<T> clazz){
		InsertAttribute ia = new InsertAttribute();
		Insert insert = (Insert)clazz.getAnnotation(Insert.class);
		if(null!=insert){
			ia.setChecked(insert.checked());
			//解析insert中的可用字段
			ia.setUsedColumns(computeUsedColumns("insert",
					entityKeyMap,
					insert.usedColumns(),insert.noUsedColumns())
				);
		}
		tmd.setInsertAttribute(ia);
	}
	/**
	 * 解析实体的table注解
	 * @param tmd
	 * @param entityKeyMap
	 * @param table
	 * @param clazz
	 */
	private static <T> void parseTableMetaData(TableMetaData tmd,Map<String,String> entityKeyMap,Table table,Class<T> clazz){
		String tmp;//临时变量
		List<String> list;
		//设置表的主键
		if(StringUtils.isNotEmpty(table.pkColumns())){
			tmd.setPkColumns(getDbFiledNameFromMap(entityKeyMap,table.pkColumns()));
		}
		//设置表的唯一约束键
		tmp = ""; list = New.list();
		for(String s : table.ukColumns()){
			tmp = getDbFiledNameFromMap(entityKeyMap,s);
			list.add(tmp);
		}
		tmd.setUkColumns(list);
		//设置自动生成的值的字段列表，这些字段将不会出现在insert列表中
		tmp = ""; list = New.list();
		for(String s : table.generatedKeyColumns()){
			tmp = getSimpleDbFiledNameFromMap(entityKeyMap,s);
			list.add(tmp);
		}
		tmd.setGeneratedKeyColumns(list);
	}
	/**
	 * 解析实体中的所有字段，并排除掉Column注解中noUsed属性的字段
	 * @param columns
	 * @param entityKeyMap
	 * @param clazz
	 */
	private static <T> void parseEntityColumns(Map<String, Column> columns,Map<String,String> entityKeyMap,Class<T> clazz,boolean isSuper){
		DataType dType;
        int m;
		for(Field f : clazz.getDeclaredFields()){
            m = f.getModifiers();
            if(m == Modifier.PRIVATE){
                j.util.jdbc.simple.annotation.Column c = f.getAnnotation(j.util.jdbc.simple.annotation.Column.class);
                String columnName = "";
                dType = DataType.AUTO;
                if(null != c){
                    //不使用该字段，直接下一个
                    if(c.noUsed())continue;
                    if(StringUtils.isNotEmpty(c.name())){
                        columnName = c.name().toUpperCase();
                    }else{
                        columnName = f.getName().toUpperCase();
                    }
                    if(f.getType().equals(java.util.Date.class)&&c.datatype().equals(DataType.AUTO)){
                        dType = DataType.DATETIME;
                    }else{
                        dType = c.datatype();
                    }
                }else{
                    columnName = f.getName().toUpperCase();
                    if(f.getType().equals(java.util.Date.class)){
                        dType = DataType.DATETIME;
                    }
                }
                Column column = new Column(columnName,f.getName());
                column.setDataType(dType);
                column.setSuperClassField(isSuper);
                if(null!=c){
                    column.setHasDefaultValue(c.hasDefaultValue());
                    column.setNullable(c.nullable());
                    column.setInsertable(c.insertable());
                    column.setUpdatable(c.updatable());
                }
                columns.put(columnName, column);
                entityKeyMap.put(f.getName().toUpperCase(), columnName);
            }
		}
        //处理完本类后，再解析父类中定义的字段
        Class<?> superClazz = clazz.getSuperclass();
        if(null!=superClazz){
            parseEntityColumns(columns,entityKeyMap,superClazz,true);
        }
	}
	/**
	 * 不检测复合主键
	 * @param map
	 * @param entityAttribute
	 * @return
	 */
	private static String getSimpleDbFiledNameFromMap(Map<String,String> map,String entityAttribute){
		String v = map.get(entityAttribute.toUpperCase());
		if(StringUtils.isNullOrEmpty(v)){
			throw new RuntimeException(String.format(INVALID_FIELD_MSG, entityAttribute));
		}
		return v;
	}
	/**
	 * 检测复合主键
	 * @param map
	 * @param entityAttribute
	 * @return
	 */
	private static String getDbFiledNameFromMap(Map<String,String> map,String entityAttribute){
		String ret = "";
		//先检查属性中是否有逗号，若有，则说明为复合键
		String[] sArr = entityAttribute.split(",");
		int i = 0;
		String v;
		for(String s : sArr){
			v = map.get(s.toUpperCase());
			if(StringUtils.isNullOrEmpty(v)){
				throw new RuntimeException(String.format(INVALID_FIELD_MSG, s));
			}
			if(i++>0){
				ret += ",";
			}
			ret += v;
		}
		return ret;
	}
	/**
	 * 获取类的名字，不包含包名
	 * @param classFullName 类的完整名字
	 * @return
	 */
	public static String getOnlyClassName(final String classFullName){
		int index = classFullName.lastIndexOf(".");
		if(index>-1){
			return classFullName.substring(index+1);
		}
		return classFullName;
	}
	/**
	 * 从usedColumns及nousedColumns计算最终的列表，当都指定时，则以usedColumns为主
	 * @param tag
	 * @param entityKeyMap
	 * @param usedColumns
	 * @param nousedColumns
	 * @return
	 */
	private static Set<String> computeUsedColumns(String tag,Map<String,String> entityKeyMap,String[] usedColumns,String[] nousedColumns){
		Set<String> list_usedColumns = New.set();
		String db_field_name;
		if(null!=usedColumns && usedColumns.length > 0){
			for(String c : usedColumns){
				db_field_name = entityKeyMap.get(c.toUpperCase());
				if(StringUtils.isNullOrEmpty(db_field_name)){
					throw new RuntimeException("entity "+tag+" annotation usedColumns "+String.format(INVALID_FIELD_MSG, c));
				}
				list_usedColumns.add(db_field_name);
			}
		}else if(null!=nousedColumns && nousedColumns.length>0){
			//将实体中的可用字段列表中去除掉noUsed中的字段
			/**
			 * 做法：1.先将noUsed数组转换为一个集合
			 * 2.将map中的values去掉这个集合并赋值给usedColumns
			 * 目标是最终计算出可用字段列表
			 */
			List<String> noUsed = New.list(nousedColumns.length);
			for(String c : nousedColumns){
				db_field_name = entityKeyMap.get(c.toUpperCase());
				if(StringUtils.isNullOrEmpty(db_field_name)){
					throw new RuntimeException("entity "+tag+" annotation noUsedColumns "+String.format(INVALID_FIELD_MSG, c));
				}
				noUsed.add(db_field_name);
			}
			List<String> _used = New.list();
			_used.addAll(entityKeyMap.values());
			_used.removeAll(noUsed);
			list_usedColumns.addAll(_used);
		}
		return list_usedColumns;
	}
}
